package com.easoft.code.pos.ui.bind;

import com.easoft.code.pos.ui.controllers.exception.ControllerException;
import com.easoft.code.pos.util.ComboItem;
import com.easoft.code.pos.util.UIUtil;
import com.easoft.code.pos.util.Valores;
import com.toedter.calendar.JDateChooser;
import java.awt.Color;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

/**
 *
 * @author Eliu
 */
public class UIBindManager {

    private static final Map<String, List<UIBinder>> bindersMap = new HashMap<>();

    private static Number parseTxt2Number(String val) {
        try {
            return NumberFormat.getInstance().parse(val);
        } catch (ParseException ex1) {
            ex1.printStackTrace(System.err);
            return null;
        }
    }

    public static Object castInputValue(Class type, String inputVal) {
        switch (type.getSimpleName()) {
            case "Integer":
                return parseTxt2Number(inputVal).intValue();
            case "Float":
                return parseTxt2Number(inputVal).floatValue();
            case "Double":
                return parseTxt2Number(inputVal).doubleValue();
            case "Date": {
                try {
                    SimpleDateFormat sd = new SimpleDateFormat(Valores.getConfigValue(Valores.DATE_FORMAT));
                    return sd.parse(inputVal);
                } catch (ParseException ex) {
                    ex.printStackTrace(System.err);
                    return null;
                }
            }

            default:
                return inputVal;
        }

    }

    /**
     *
     * @param binder
     */
    public static void doBindObject2Input(UIBinder binder) {
        try {
            System.out.println("object2input " + binder.toString());
            String getMethod = "g" + binder.getTarMethod().getName().substring(1);
            Method m = binder.getTarObject().getClass().getMethod(getMethod, new Class[]{});
            Object val = m.invoke(binder.getTarObject(), new Object[]{});
            if (val != null) {
                if (binder.getTarInput() instanceof JDateChooser) {
                    ((JDateChooser) binder.getTarInput()).setDate((Date) val);
                } else if (binder.getTarInput() instanceof JComboBox) {
                    JComboBox cm = ((JComboBox) binder.getTarInput());
                    Object frstItem = cm.getItemAt(0);

                    if (frstItem instanceof ComboItem) { //predefined object
                        ComboItem cItem = new ComboItem(val.toString(), null);
                        cm.getModel().setSelectedItem(cItem);
                    } else { //anonymous object
                        cm.getModel().setSelectedItem(val);
                    }
                    cm.validate();
                } else {
                    ((JTextField) binder.getTarInput()).setText(val.toString());
                }
            }
        } catch (IllegalAccessException | IllegalArgumentException | NoSuchMethodException | SecurityException | InvocationTargetException ex) {
            Logger.getLogger(UIBindManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     *
     * @param key
     */
    public static void masiveBindObject2Input(String key) {
        List<UIBinder> ls = bindersMap.get(key);
        if (ls != null) {
            for (UIBinder binder : ls) {
                doBindObject2Input(binder);
            }
        }
    }

    /**
     * exec bind relation ship between input and binded object
     *
     * @param binder
     * @throws com.easoft.code.pos.ui.bind.BindValidation
     */
    public static void doBindInput2Object(UIBinder binder) throws BindValidation {
        try {
            Class[] pTypes = binder.getTarMethod().getParameterTypes();
            Class type = pTypes[0];
            binder.setLabel(binder.getLabel() != null ? binder.getLabel() : binder.getTarInput().getName());
            if (binder.getTarInput() instanceof JTextField) {
                if (binder.isRequired() && ((JTextField) binder.getTarInput()).getText().isEmpty()) {
                    throw new BindValidation(binder.getLabel().concat(" es requerido!"));
                }
                binder.getTarMethod().invoke(binder.getTarObject(),
                        castInputValue(type, ((JTextField) binder.getTarInput()).getText()));
            } else if (binder.getTarInput() instanceof JDateChooser) {
                if (binder.isRequired() && ((JDateChooser) binder.getTarInput()).getDate() == null) {
                    throw new BindValidation(binder.getLabel().concat(" es requerido!"));
                }
                binder.getTarMethod().invoke(binder.getTarObject(),
                        ((JDateChooser) binder.getTarInput()).getDate());

            } else if (binder.getTarInput() instanceof JRadioButton) {
                binder.getTarMethod().invoke(binder.getTarObject(),
                        ((JRadioButton) binder.getTarInput()).getText());
            } else {
                Object selectedItem = ((JComboBox) binder.getTarInput()).getSelectedItem();
                if (binder.isRequired() && selectedItem == null) {
                    throw new BindValidation(binder.getLabel().concat(" es requerido!"));
                }
                if (selectedItem instanceof ComboItem) { //predefined object
                    binder.getTarMethod().invoke(binder.getTarObject(),
                            castInputValue(type, ((ComboItem) selectedItem).getKey()));
                } else { //anonymous object
                    binder.getTarMethod().invoke(binder.getTarObject(),
                            type.cast(selectedItem));
                }
            }

        } catch (IllegalAccessException | InvocationTargetException ex) {
            Logger.getLogger(UIBindManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * exec binders list from related key
     *
     * @param key
     * @throws com.easoft.code.pos.ui.controllers.exception.ControllerException
     */
    public static void masiveBindInput2Object(String key) throws ControllerException {
        List<UIBinder> ls = bindersMap.get(key);
        if (ls != null) {
            StringBuilder erros = new StringBuilder();
            for (UIBinder binder : ls) {
                try {
                    doBindInput2Object(binder);
                } catch (BindValidation ex) {
                    erros.append(ex.getMessage().concat("\n"));
                }
            }
            if (erros.length() > 0) {
                UIUtil.showError("Errores de validacion: \n" + erros.toString(), "Validaciones");
                throw new ControllerException("Error masivo validaciones", null);
            }
        }
    }

    public static void add(UIBinder binder, String key) {
        if (bindersMap.get(key) == null) {
            bindersMap.put(key, new LinkedList());
        }
        bindersMap.get(key).add(binder);
        bind(binder);
    }

    public static void remove(String key) {
        bindersMap.remove(key);
    }

    private static void bind(final UIBinder binder) {
        doBindObject2Input(binder);
        binder.getTarInput().addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusLost(java.awt.event.FocusEvent evt) {
                try {
                    doBindInput2Object(binder);
                } catch (BindValidation ex) {
                    System.out.println("error.validacion.=" + ex.getMessage());
                    ((JComponent) evt.getComponent()).setBackground(Color.red);
                }
            }
        });
    }

}
